package cn.ccbom.bom.interview.util.script.script;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.URLUtil;
import jdk.nashorn.api.scripting.NashornScriptEngine;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;

import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.SimpleBindings;
import javax.script.SimpleScriptContext;
import java.net.URL;
import java.util.function.Consumer;

public class EngineClient {
    private final NashornScriptEngine engine;

    public EngineClient() {
        //1. 获取引擎
        NashornScriptEngineFactory factory = EngineUtil.getNashornScriptEngineFactory();
        this.engine = (NashornScriptEngine) factory.getScriptEngine();

        //2. 脚本上下文,多脚本执行可共享
        ScriptContext scriptContext = new SimpleScriptContext();
        SimpleBindings globalBindings = EngineUtil.globalBindings();
        scriptContext.setBindings(globalBindings, ScriptContext.GLOBAL_SCOPE);
        this.engine.setContext(scriptContext);//引擎范围共享

    }

    public Object execute(String name, String script) {
        return execute(name, script, false);
    }

    public Object execute(String name, String script, boolean share) {
        SimpleScriptContext context = new SimpleScriptContext();
        Object result = execute(name, script, context);
        if (share) {
            Bindings engineBindings = engine.getContext().getBindings(ScriptContext.ENGINE_SCOPE);
            engineBindings.putAll(context.getBindings(ScriptContext.ENGINE_SCOPE));
        }
        return result;
    }


    public Object execute(String name, String script, ScriptContext scriptContext) {//局部上下文,导入和当前脚本共享变量
        scriptContext.setBindings(engine.getBindings(ScriptContext.GLOBAL_SCOPE),ScriptContext.GLOBAL_SCOPE);
        //导入规则
        scriptContext.setAttribute("_import", (Consumer<String>) path -> {
            try {
                if (path.contains(":")) {
                    URL u = URLUtil.url(path);
                    String _import = IoUtil.readUtf8(u.openStream());
                    scriptContext.setAttribute("javax.script.filename", path, ScriptContext.ENGINE_SCOPE);
                    engine.eval(_import, scriptContext);
                }
            } catch (Exception e) {
                throw new EngineException(e);
            }
        }, ScriptContext.ENGINE_SCOPE);

        try {
            scriptContext.setAttribute("javax.script.filename", name, ScriptContext.ENGINE_SCOPE);
            //synchronized (scriptContext.getBindings(ScriptContext.ENGINE_SCOPE)) {//必须要上锁
            return this.engine.eval(script, scriptContext);//各自局部变量 不会冲突
            //}
        } catch (Exception e) {
            throw new EngineException(e);
        }
    }

    public Object executeUrl(String name, String url) {
        URL u = URLUtil.url(url);
        String script;
        try {
            script = IoUtil.readUtf8(u.openStream());
        } catch (Exception e) {
            throw new EngineException("get url content error", e);
        }
        return execute(name, script, true);
    }

    public Object invoke(String name, Object... params) {
        try {
            engine.getContext().setAttribute("javax.script.filename", name, ScriptContext.ENGINE_SCOPE);
            return this.engine.invokeFunction(name, params);
        } catch (Exception e) {
            throw new EngineException(e);
        }
    }
}
